﻿//-----------------------------------------------------------------------------------------------------------
// Wbfs Engine (WBFSe) Project by Omega Frost 
// http://wbfse.codeplex.com/
//
// WBFSe is Licensed under the terms of the 
// Microsoft Reciprocal License (Ms-RL)
//-----------------------------------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using WBFSe.Attributes;
using WBFSe.IO;

namespace WBFSe.FormatApi
{
    /// <summary>
    /// Format reader used by the engine
    /// </summary>
    public abstract class WbfsReader : IDisposable
    {
        // Variables

        private SemaphoreSlim semaphore;
        private List<IWbfsStream> activeStreams;

        protected String lastError;
        protected String path;
        protected List<String> filenames;
        
        protected int version;

        protected int sectorSize;
        protected UsageTable sectorUsage;

        protected long fileSize;

        protected GameType gameType;
        protected String gameCode;
        protected String gameName;
        protected Region gameRegion;

        // Properties

        /// <summary>
        /// String with the last error encountered by the reader
        /// </summary>
        public String LastError
        {
            get { return this.lastError; }
        }

        /// <summary>
        /// The number of active streams in use by the program
        /// </summary>
        public int ActiveStreams
        {
            get { return this.activeStreams.Count; }
        }

        /// <summary>
        /// Version of the container
        /// </summary>
        public int Version
        {
            get { return this.version; }
        }

        /// <summary>
        /// Location of the container
        /// </summary>
        public String Path
        {
            get { return this.path; }
        }

        /// <summary>
        /// Readonly list of the container files
        /// </summary>
        public IList<String> Filenames
        {
            get { return this.filenames.AsReadOnly(); }
        }

        /// <summary>
        /// Sector size used in the container
        /// </summary>
        public int SectorSize
        {
            get { return this.sectorSize; }
        }

        /// <summary>
        /// Game sectors stored in the container
        /// </summary>
        public UsageTable SectorUsage
        {
            get { return this.sectorUsage; }
        }

        /// <summary>
        /// The type of the game stored in the container
        /// </summary>
        public GameType GameType
        {
            get { return this.gameType; }
        }

        /// <summary>
        /// The name of the game stored in the container
        /// </summary>
        public String GameCode
        {
            get { return this.gameCode; }
        }

        /// <summary>
        /// The code of the game stored in the container
        /// </summary>
        public String GameName
        {
            get { return this.gameName; }
        }

        /// <summary>
        /// The region of the game stored in the container
        /// </summary>
        public Region GameRegion
        {
            get { return this.gameRegion; }
        }

        /// <summary>
        /// Physical size of the container
        /// </summary>
        public long FileSize
        {
            get { return this.fileSize; }
        }

        // Methods

        /// <summary>
        /// Constructor
        /// </summary>
        public WbfsReader()
        {
            this.activeStreams = new List<IWbfsStream>();
            this.semaphore = new SemaphoreSlim(3);
        }

        /// <summary>
        /// Creates a stream with desired access and share
        /// </summary>
        /// <param name="access">Access for all files of the container</param>
        /// <param name="share">Share for all files of the container</param>
        /// <returns>Stream</returns>
        /// <exception cref="System.Exception">On operation failure</exception>
        public IWbfsStream Open(FileAccess access, FileShare share)
        {
            // Try get the stream
            IWbfsStream stream = GetStream(access, share);
            if (stream == null) throw new Exception(this.LastError);

            // Set up the notification event
            stream.Closing += new StreamClosingDelegate(StreamClosing);

            // Thread synchronization
            this.semaphore.Wait();

            // Add the stream to the list
            this.activeStreams.Add(stream);

            // Thread synchronization
            this.semaphore.Release();

            return stream;
        }

        /// <summary>
        /// Finds the container files in the array
        /// </summary>
        /// <param name="directory">The source folder</param>
        /// <param name="files">The files inside the source folder</param>
        /// <param name="filename">The desired filename</param>
        /// <exception cref="System.FileNotFoundException">If filename not found</exception>
        /// <exception cref="System.Exception">On operation failure</exception>
        public abstract void GetFiles(String directory, String[] files, String filename);

        /// <summary>
        /// Parses the container
        /// </summary>
        /// <exception cref="System.Exception">On operation failure</exception>
        public abstract void ParseContainer();

        /// <summary>
        /// Verifies if there's a game inside the container,
        /// should be used only by multi purpose containers
        /// such as RAR, ZIP, ISO. Avoid throwing exceptions
        /// inside this method, it can lead to poor engine
        /// performance on reading multiple files
        /// </summary>
        /// <returns>True if there's a game in the container</returns>
        public abstract bool IsGame();

        /// <summary>
        /// Changes the attributes of the game in the container's header
        /// </summary>
        /// <param name="name">The new name of the game</param>
        /// <param name="code">The new code of the game</param>
        /// <param name="region">The new region of the game</param>
        public abstract void ChangeHeader(String name, String code, Region region);

        /// <summary>
        /// Creates a Stream from the container files
        /// </summary>
        /// <param name="access">Desired acess</param>
        /// <param name="share">Desired sharing</param>
        /// <returns>Stream</returns>
        /// <exception cref="System.Exception">On operation failure</exception>
        protected abstract IWbfsStream GetStream(FileAccess access, FileShare share);

        /// <summary>
        /// Cancels pending operations and closes streams
        /// </summary>
        protected abstract void Closing();

        /// <summary>
        /// Removes the stream from the list
        /// </summary>
        /// <param name="sender">Closing Stream</param>
        private void StreamClosing(IWbfsStream sender)
        {
            // Thread synchronization
            this.semaphore.Wait();

            // Remove the stream from the list
            this.activeStreams.Remove(sender);

            // Thread synchronization
            this.semaphore.Release();
        }

        /// <summary>
        /// Forces all streams to close
        /// </summary>
        public void Close()
        {
            foreach (IWbfsStream stream in this.activeStreams)
                stream.Close();
        }

        /// <summary>
        /// Disposes the container
        /// </summary>
        public abstract void Dispose();
    }
}
